/*******************************************************************************
 * Copyright (c) 2004 Andrei Loskutov.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the BSD License
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/bsd-license.php
 * Contributor:  Andrei Loskutov - initial API and implementation
 *******************************************************************************/
/* This class is started as extension of Rahul Kuchal's whitespace plugin.
 * Rahul Kuchal - http://www.kuchhal.com/ */

package de.loskutov.anyedit.popup.actions;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentRewriteSession;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionDelegate;
import org.eclipse.ui.IEditorActionDelegate;
import org.eclipse.ui.IEditorPart;

import com.googlecode.eclipse.navigatorext.Activator;
import com.googlecode.eclipse.navigatorext.l10n.Messages;

public abstract class AbstractTextAction extends AbstractAction implements IEditorActionDelegate {

    public void setActiveEditor(IAction action, IEditorPart targetEditor) {
        if (targetEditor == null) {
            return;
        }
        setEditor(createActiveEditorDelegate(targetEditor));
    }

    protected abstract TextReplaceResultSet estimateActionRange(IDocument doc);

    /**
     * @see IActionDelegate#run(IAction)
     */
    public final void run(IAction action) {
        super.run(action);

        AbstractEditor currEditor = getEditor();
        if (currEditor == null) {
            return;
        }

        IDocument doc = currEditor.getDocument();
        if (doc == null) {
            return;
        }

        TextReplaceResultSet result = estimateActionRange(doc);

        // no lines affected - return immediately
        if (result.getNumberOfLines() == 0) {
            return;
        }

        // save dirty buffer, if enabled and if this action is not chained with
        // "save" operation
        if (currEditor.isDirty() && isSaveDirtyBufferEnabled()) {
            IProgressMonitor monitor = new NullProgressMonitor();
            currEditor.doSave(monitor);
            if (monitor.isCanceled()) {
                boolean ok = MessageDialog.openConfirm(Activator.getShell(), Messages.Dialog_title_err,
                        Messages.Dialog_msg_continueOperation);
                if (!ok) {
                    return;
                }
            }
        }

        try {
            doTextOperation(doc, action.getId(), result);
        } catch (Exception ex) {
            Activator.errorDialog(null, ex);
            return;
        }

        if (!result.areResultsChanged()) {
            return;
        }

        // prepare to save: make sure, that file is not readonly
        currEditor.validateEditorInputState();
        if (!currEditor.isEditorInputModifiable()) {
            Shell shell = Activator.getShell();
            MessageDialog.openInformation(shell, Messages.Dialog_title_err, Messages.Dialog_msg_fileIsReadOnly);
            return;
        }

        int docLinesNbr = doc.getNumberOfLines();
        int changedLinesNbr = result.getNumberOfLines();
        boolean rewriteWholeDoc = changedLinesNbr >= docLinesNbr;
        DocumentRewriteSession rewriteSession = currEditor.startSequentialRewriteMode(rewriteWholeDoc);

        // some oddities with document??? prevent overflow in changedLinesNbr
        if (rewriteWholeDoc) {
            changedLinesNbr = docLinesNbr;
        }

        /*
         * TODO think on long running operations
         */
        // if (changedLinesNbr > 150) {
        // Display display=
        // getTextEditor().getEditorSite().getWorkbenchWindow().getShell().getDisplay();
        // BusyIndicator.showWhile(display, runnable);
        // } else
        // runnable.run();
        try {
            for (int i = 0; i < changedLinesNbr; i++) {
                LineReplaceResult trr = result.get(i);
                if (trr != null) {
                    IRegion lineInfo = doc.getLineInformation(i + result.getStartLine());
                    doc.replace(lineInfo.getOffset() + trr.startReplaceIndex, trr.rangeToReplace, trr.textToReplace);
                }
            }
        } catch (Exception ex) {
            Activator.errorDialog(null, ex);
        } finally {
            currEditor.stopSequentialRewriteMode(rewriteSession);
            // seems to have problems with StructuredTextReconciler
            // if(partitioners != null){
            // TextUtilities.addDocumentPartitioners(doc, partitioners);
            // }
            result.clear();
        }
    }

    /**
     * Should be invoked always after estimateActionRange() to ensure that operaton is possible
     * 
     * @param doc cannot be null
     * @param actionID desired text action id
     * @param resultSet cannot be null
     */
    protected abstract void doTextOperation(IDocument doc, String actionID, TextReplaceResultSet resultSet)
            throws BadLocationException;

    protected static boolean isSaveDirtyBufferEnabled() {
        return true;
    }

}
